package com.anuo.app.study.studymultithread;

import java.util.concurrent.*;

/**
 * 多线程
 */
public class StudyMultithread {

    public static void main(String[] args) {
        createThread();
        //studyThreadPool();

    }

    /**
     * 创建线程 并传参数
     * 如果需要传递多个参数, 在构造函数里面加多个就是了
     */
    public static void createThread() {
        //创建线程常见有3种方式见下面, 一般是用1,3种来创建线程
        //其中两种方式的比较 https://stackoverflow.com/questions/541487/implements-runnable-vs-extends-thread

        //1.实现Runnable接口 创建线程
//        CreateThreadByImplementRunnable threadA =
//                new CreateThreadByImplementRunnable("threadA"/*传参数 threadA*/);
//        threadA.start();
//        CreateThreadByImplementRunnable threadB = new CreateThreadByImplementRunnable("threadB");
//        threadB.start();

        //2. 继承Thread类创建线程
//        CreateThreadByExtendsThread threadByExtendsThread = new CreateThreadByExtendsThread();
//        threadByExtendsThread.start();
//        CreateThreadByExtendsThread threadByExtendsThread2 = new CreateThreadByExtendsThread();
//        threadByExtendsThread2.start();

        //3. FutureTask+Callable+Lambda实现并返回结果
        //声明
        FutureTask futureTask = new FutureTask((Callable) () -> {
            System.out.println("hello world");
            System.out.println("当前线程名字:" + Thread.currentThread().getName());
            return "hello";
        });
        //运行
        new Thread(futureTask, "线程A").start();
        //获取结果
        try {
            String msg = futureTask.get().toString();
            System.out.println("运行结果:" + msg);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }

    /**
     * lambda 创建线程 不能返回结果(一般不用这种)
     */
    public static void createThreadbyLambda() {
        String param1 = "hello";
        String param2 = "world";
        // lambda 创建
        Runnable task = () -> {
            String msg = param1 + param2;
            System.out.println(msg);
        };
        Thread thread = new Thread(task);
        thread.start();
        //一般创建
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("搬砖");
            }
        };
        Thread thread1 = new Thread(runnable);
        thread1.start();

    }


    /**
     * wait, notify 和 notifyAll 线程间协作 例子
     * 参考: https://stackoverflow.com/questions/2536692/a-simple-scenario-using-wait-and-notify-in-java
     */
    private static class studyCollaboration {

        private boolean pizzaArrived = false;

        public void eatPizza() throws InterruptedException {
            synchronized (this) {
                while (!pizzaArrived) {
                    wait();
                }
            }
            System.out.println("yumyum..");
        }

        public void pizzaGuy() {
            synchronized (this) {
                this.pizzaArrived = true;
                notifyAll();
            }

        }
    }

    /**
     * 线程状态:
     * 执行Start()方法 就绪状态
     * --获得CPU资源后--> 执行 run() 方法 运行状态 --> 如果收到了阻塞 , 进入阻塞状态 , 阻塞完了回到就绪状态
     * --> 运行完 死亡状态
     * 参考:  http://www.runoob.com/java/java-multithreading.html
     */


    /**
     * 获取线程的执行结果 用 Callable、Future
     * 参考: https://stackoverflow.com/questions/3141158/how-can-a-thread-return-a-value-after-finishing-its-job
     * http://www.cnblogs.com/dolphin0520/p/3949310.html
     */
    public static void getThreadResult() {
        int threadNum = Runtime.getRuntime().availableProcessors() * 2;
        ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
        Future<String> task = executorService.submit(new FooTask());
        try {
            String result = task.get();// this raises ExecutionException if thread dies
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        executorService.shutdown();
    }

    /**
     * 研究线程的 Join 方法, Join方法的作用是等待一个线程执行完后, 再往后执行
     * (即先阻塞主线程当调用Join方法的这个线程执行完了,再恢复主线程往后执行)
     * 具体见: https://www.javatpoint.com/join()-method 的第一个例子(解释得非常好),
     * 或者下面的这个例子
     */
    public static void StudyJoin() {
        Thread t1 = new Thread(new MyRunnable(), "t1");
        Thread t2 = new Thread(new MyRunnable(), "t2");
        Thread t3 = new Thread(new MyRunnable(), "t3");

        t1.start();

        //start second thread after waiting for 2 seconds or if it's dead
        try {
            t1.join(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t2.start();

        //start third thread only when first thread is dead
        try {
            t1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t3.start();

        //let all threads finish execution before finishing main thread
        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("All threads are dead, exiting main thread");
    }


    /**
     * 继承Thread类创建线程
     */
    private static class CreateThreadByExtendsThread extends Thread {
        private static int threadNum;

        public CreateThreadByExtendsThread(String threadName) {
            super.setName(threadName);
            threadNum++;
        }

        @Override
        public void run() {
            System.out.println("你继承Thread类创建的第" + threadNum + "个线程");
        }
    }

    /**
     * 实现 Runnable接口 创建线程
     */
    public static class WorkThread implements Runnable {
        private String threadName;

        public WorkThread(String threadName) {
            this.threadName = threadName;
        }

        @Override
        public void run() {

            try {
                System.out.println("线程:" + threadName + " 搬砖中...");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 实现 Callable 接口 创建可以获取返回值的线程
     */
    private static class FooTask implements Callable<String> {
        @Override
        public String call() throws Exception {
            Thread.sleep(6 * 1000);
            return "hello world";
        }
    }

    /**
     * 实现 Runnable接口 创建线程 为 StudyJoin 方法服务
     */
    private static class MyRunnable implements Runnable {

        @Override
        public void run() {
            System.out.println("Thread started:::" + Thread.currentThread().getName());
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread ended:::" + Thread.currentThread().getName());
        }

    }

    /**
     * 线程异常处理
     */
    static class CreateThreadByImplementRunnable implements Runnable {
        private Thread thread;
        private String threadName;

        public CreateThreadByImplementRunnable(String threadName) {
            this.threadName = threadName;
            System.out.println("线程:" + threadName + " 已创建");
        }

        @Override
        public void run() {
            try {
                for (int i = 0; i < 6; i++) {
                    System.out.println("线程:" + threadName + " 执行:" + i);
                    Thread.sleep(100);
                }
                throw new RuntimeException("fuck" + threadName);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        /**
         * 线程异常处理
         * 思路: 先定义一个 ExceptionHandler 叫threadHandler,然后在线程start()前设置异常处理handler 为threadHandler
         * 这样当线程出现异常的时候就会捕获到
         * 参考: https://stackoverflow.com/questions/6546193/how-to-catch-an-exception-from-a-thread
         */
        public void start() {
            //定义异常handler
            Thread.UncaughtExceptionHandler threadHandler = new Thread.UncaughtExceptionHandler() {
                public void uncaughtException(Thread th, Throwable ex) {
                    System.out.println("Uncaught exception: " + ex);
                }
            };

            System.out.println("我开始了线程:" + threadName);
            if (thread == null) {
                thread = new Thread(this, threadName);
                thread.setUncaughtExceptionHandler(threadHandler);//设置异常handler
                thread.start();
            }

        }

//        public void test(){
//            CreateThreadByExtendsThread thread = new CreateThreadByExtendsThread();
//            //thread.setUncaughtExceptionHandler();
//        }
    }
}